package br.com.ps.bombertournament.game.object;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import br.com.ps.bombertournament.game.MappedGameObject;
import br.com.ps.bombertournament.game.WrapperMap;

public class FactoryGameObject {
	
	private enum MapObject {
		PLAYER_BOMBERMAN(Player.class, "data/player/bomberman/bomberman.json"),
		PLAYER_LINK(Player.class, "data/player/link/link.json"),
		PLAYER_PETIT_GROS(Player.class, "data/player/petit_gros/petit_gros.json"),
		PLAYER_PETIT_MAIGRE(Player.class, "data/player/petit_maigre/petit_maigre.json"),
		PLAYER_KIT_KAT(Player.class, "data/player/kit_kat/kit_kat.json"),
		
		BOMB(Bomb.class),
		FLOOR(Floor.class),
		SUPER_WALL(SuperWall.class),
		WALL(Wall.class),
		EMPTY(Empty.class),
		ITEM(Item.class),
		
		BOMB_FIRE_CENTER(BombFire.class, "data/world/fire/center.json"),
		BOMB_FIRE_UP(BombFire.class, "data/world/fire/up.json"),
		BOMB_FIRE_UP_EXT(BombFire.class, "data/world/fire/up_ext.json"),
		BOMB_FIRE_DOWN(BombFire.class, "data/world/fire/down.json"),
		BOMB_FIRE_DOWN_EXT(BombFire.class, "data/world/fire/down_ext.json"),
		BOMB_FIRE_RIGHT(BombFire.class, "data/world/fire/right.json"),
		BOMB_FIRE_RIGHT_EXT(BombFire.class, "data/world/fire/right_ext.json"),
		BOMB_FIRE_LEFT(BombFire.class, "data/world/fire/left.json"),
		BOMB_FIRE_LEFT_EXT(BombFire.class, "data/world/fire/left_ext.json");

		private Class<?> clazz;
		private Map<String, Object> cache = new HashMap<String, Object>();
		private String type;

		private MapObject(Class<?> clazz, String type) {
			this.clazz = clazz;
			this.type = type;
		}
		
		private MapObject(Class<?> clazz) {
			this.clazz = clazz;
		}
		
		public Object getObject() {
			return cache.get(type);
		}
		
		public Object getObject(String type) {
			return cache.get(type);
		}

		public Object newInstance(String type) {
			Object result = null;
			
			try {
				Constructor<?> contructor = clazz.getConstructor(String.class);
				
				result = contructor.newInstance(type);
			} catch (Exception e) {
				try {
					Constructor<?> contructor = clazz.getConstructor(WrapperMap.class);
					
					Map<String, Object> map = new HashMap<String, Object>();
					map.put("configurations", type);
					
					result = contructor.newInstance(new WrapperMap(map));
				} catch (Exception e1) {
					try {
						result = clazz.newInstance();
					} catch (InstantiationException e2) {
						e2.printStackTrace();
					} catch (IllegalAccessException e2) {
						e2.printStackTrace();
					}
				}
			}
			
			cache.put(type, result);
			
			return result;
		}
		
		public Object newInstance() {
			return newInstance(type);
		}
		
		public Object newClone(String type) {
			Object result = null;
			
			if (cache.get(type) == null) {
				newInstance();
			}
			
			try {
				Method cloneMethod = clazz.getMethod("clone");
				
				result = cloneMethod.invoke(cache.get(type));
			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
			
			return result;
		}
		
		public Object newClone() {
			return newClone(type);
		}
	}
	
	public static BombFire createBombFireCenter() {
		return createObject(MapObject.BOMB_FIRE_CENTER);
	}
	
	public static BombFire createBombFireLeft() {
		return createObject(MapObject.BOMB_FIRE_LEFT);
	}
	
	public static BombFire createBombFireLeftExt() {
		return createObject(MapObject.BOMB_FIRE_LEFT_EXT);
	}
	
	public static BombFire createBombFireRight() {
		return createObject(MapObject.BOMB_FIRE_RIGHT);
	}
	
	public static BombFire createBombFireRightExt() {
		return createObject(MapObject.BOMB_FIRE_RIGHT_EXT);
	}
	
	public static BombFire createBombFireUp() {
		return createObject(MapObject.BOMB_FIRE_UP);
	}
	
	public static BombFire createBombFireUpExt() {
		return createObject(MapObject.BOMB_FIRE_UP_EXT);
	}
	
	public static BombFire createBombFireDown() {
		return createObject(MapObject.BOMB_FIRE_DOWN);
	}
	
	public static BombFire createBombFireDownExt() {
		return createObject(MapObject.BOMB_FIRE_DOWN_EXT);
	}
	
	public static Bomb createBomb(int power) {
		Bomb result = createObject(MapObject.BOMB);
		
		result.setPower(power);
		
		return result;
	}
	
	public static Player createPlayerBomberman() {
		return createObject(MapObject.PLAYER_BOMBERMAN);
	}
	
	public static Floor createFloor(String type) {
		return createObject(MapObject.FLOOR, type);
	}
	
	public static SuperWall createSuperWall(String type) {
		return createObject(MapObject.SUPER_WALL, type);
	}
	
	public static Empty createController() {
		return createObject(MapObject.EMPTY, "data/world/controller/controller.json");
	}
	
	public static Empty createButtonBomb() {
		return createObject(MapObject.EMPTY, "data/world/controller/button_bomb.json");
	}
	
	public static Item createItem(String type) {
		return createObject(MapObject.ITEM, type);
	}
	
	public static Wall createWall(String type) {
		return createObject(MapObject.WALL, type);
	}
	
	private static <T> T createObject(MapObject mapObject, String type) {
		Object result = mapObject.getObject();
		if (result == null) {
			if (type == null) {
				result = mapObject.newInstance();
			} else {
				result = mapObject.newInstance(type);
			}
			
			if (result instanceof MappedGameObject) {
				((MappedGameObject) result).loadAnimations();
			}
		}
		
		if (type == null) {
			result = mapObject.newClone();
		} else {
			result = mapObject.newClone(type);
		}
		
		return (T) result;
	}

	private static <T> T createObject(MapObject mapObject) {
		return createObject(mapObject, null);
	}
}